

Berta.EditorManager.addPlugin('Number', new Class(function() {
	
	return {
		extend: Berta.Editor,
		pluginName: 'Number',
		
		// defaults for this particular plugin
		defaults: {
			pluginOptions: {
				draggable: true,	// if true, value can be changed by dragging (like in Adobe CS programs)
				min: false,			// minimal value (false = no minimal value)
				max: false,			// maximal value (false = no maximal value)
				precision: 2,		// how many decimal digits to allow (0 = only integers; false = don't round)
				
				unit: false
				// see defaults.pluginOptions in Berta.Editor class for inherited options
			}
		},
		
		isDragOperation: false,
		drag: { },
		
		constructor: function(options) {
			// call the parent with pluginName=null
			// Editor defaults get extended by this.defaults and then extended by options
			Berta.Editor.call(this, options);
			
			this.originalValue = valueFromHTML(this.element.html());
			this.value = this.originalValue;
			
			var u = this.options.pluginOptions.unit;
			if (u) {
				var uA = [];
				for (var i = 0; i < u.length; i++) {
					if (u[i] != ',' && uA.indexOf(u[i]) < 0) uA.push(u[i]);
				}
				this.nonAllowedCharsRegExp = new RegExp("[^0-9." + uA.join() + "]+");
				this.unitsAvailable = u.toLowerCase().split(',');
				
			} else {
				this.nonAllowedCharsRegExp = /[^0-9.]+/;
				this.unitsAvailable = false;
			}
				
			
						
			this.draggableOptions = { 
				revert: false, 
				helper: this._(getDragHelper),
				scroll: false,
				start: this._(onDragStart),
				drag: this._(onDrag),
				stop: this._(onDragStop),
				delay: 200
			};
		},
		
		
		//   .______    __    __  .______    __       __    ______    ////////////////////////////////////////////////////////////
		//   |   _  \  |  |  |  | |   _  \  |  |     |  |  /      |   ////////////////////////////////////////////////////////////
		//   |  |_)  | |  |  |  | |  |_)  | |  |     |  | |  ,----'   ////////////////////////////////////////////////////////////
		//   |   ___/  |  |  |  | |   _  <  |  |     |  | |  |        ////////////////////////////////////////////////////////////
		//   |  |      |  `--'  | |  |_)  | |  `----.|  | |  `----.   ////////////////////////////////////////////////////////////
		//   | _|       \______/  |______/  |_______||__|  \______|   ////////////////////////////////////////////////////////////
		
		on: /* API */ function() {
			this.element.addClass('x-editable-on x-editable-number');
			if(this.options.pluginOptions.draggable) $(this.element).draggable(this.draggableOptions);
			
			this.element.html(valueForHTML(this.element.html()));
			this.element.bind('click' + this.namespace(), this._(onElementClick));
			this.appendPlaceholderIfEmpty();
			Berta.Editor.prototype.on.call(this);
		},
		
		off: /* API */ function() {
			if(this.hasFlag(Berta.Editor.flags.EDITING)) {
				this.cancel();
			}
			this.element.removeClass('x-editable-on x-editable-number');
			this.element.unbind('click' + this.namespace());
			this.element.html(valueFromHTML(this.element.html()));
			if(this.options.pluginOptions.draggable) $(this.element).draggable('destroy');
			this.removePlaceholder();
			Berta.Editor.prototype.off.call(this);
		},
		
		edit: /* API */ function() {
			if(this.hasFlag(Berta.Editor.flags.ON) && !this.hasFlag(Berta.Editor.flags.EDITING)) {
				//$(this.element).draggable('disable');
				Berta.Editor.prototype.startEditing.call(this);
				this._(startEditing)();
				Berta.Editor.prototype.setPanelContext.call(this);
			}
		},
		
		save: /* API */ function() {
			if(this.hasFlag(Berta.Editor.flags.EDITING) || this.hasFlag('DRAGGING')) {
				this.value = this.getValue();
				
				// put the value in the HTML
				if (this.hasFlag('DRAGGING')) 
					this.element.html(valueForHTML(this.value));
				else 
					this.replacementElement.html(valueForHTML(this.value));
				
				if(this.value != this.originalValue) this.fireChange();
				if (this.hasFlag(Berta.Editor.flags.EDITING)) {
					this._(stopEditing)();
					Berta.Editor.prototype.stopEditing.call(this);
				}
				
				Berta.Editor.prototype.save.call(this, this._(onSaveComplete, this.hasFlag('DRAGGING')));
			}
		},
		
		cancel: /* API */ function() {
			Berta.Editor.prototype.hidePanelContext.call(this);
			if(this.hasFlag(Berta.Editor.flags.EDITING)) {
				this._(stopEditing)();
			
			} else if(this.hasFlag(Berta.Editor.flags.SAVING)) {
				this.saveStop();
				this.element.html(valueForHTML(this.originalValue));
			
			} else {
				this.element.html(valueForHTML(this.originalValue));
				
			}
			this.value = this.originalValue;
			this.fireRevert();
			this.cleanup();
		},

		
		cleanup: function() {
			if (this.replacementElement) {
				this.replacementElement.after(this.element).empty().remove();
				this.replacementElement = null;
				this.element.data(Berta.Editor.DATA_REPLACEMENT, null);
			}
			
			if(this.hasFlag(Berta.Editor.flags.DESTROYING)) {
				this.off();
			} else {
				this.appendPlaceholderIfEmpty();
			}
		},
		
		
		
		
		validate: /* override */ function() {
			Berta.Editor.prototype.validate.call(this);
			
			// when only text allowed then allowed_tags should be "<br>"
			// when html is allowed, then add the <br> only if allowed_tags is not empty (which means: all tags are allowed)
			if(this.options.newlines && this.options.tags_allowed !== true && (!this.options.tags_allowed || this.options.tags_allowed.indexOf('<br>') < 0))
				this.options.tags_allowed = this.options.tags_allowed ? (this.options.tags_allowed + '<br>') : '<br>';
		},


		getValue: function() {
			var v, u, o = this.options.pluginOptions;
			if (this.hasFlag(Berta.Editor.flags.EDITING) || this.hasFlag('DRAGGING')) {
				if (this.hasFlag('DRAGGING')) {
					v = this.drag.value;
					u = this.drag.unit;
				} else {
					this._(htmlCheck)(); // remove unneeded chars
					var vParsed = this._(parseIntoNumberAndUnit)(this.replacementElement.html());
					v = parseFloat(vParsed.value);
					if(isNaN(v)) v = 0;
					
					u = vParsed.unit;
				}
				
				if (o.min !== false && v < o.min) v = o.min;
				if (o.max !== false && v > o.max) v = o.max;
				if (o.precision !== false) v = Math.round(v * Math.pow(10, o.precision)) / Math.pow(10, o.precision);
				
				return v + u;
					
			} else {
				return this.value;
 			
			}
		}
		
	};
	
	
	
	
	//   .______   .______     __  ____    ____  ___   .___________. _______    //////////////////////////////////////////////////
	//   |   _  \  |   _  \   |  | \   \  /   / /   \  |           ||   ____|   //////////////////////////////////////////////////
	//   |  |_)  | |  |_)  |  |  |  \   \/   / /  ^  \ `---|  |----`|  |__      //////////////////////////////////////////////////
	//   |   ___/  |      /   |  |   \      / /  /_\  \    |  |     |   __|     //////////////////////////////////////////////////
	//   |  |      |  |\  \--.|  |    \    / /  _____  \   |  |     |  |____    //////////////////////////////////////////////////
	//   | _|      | _| `.___||__|     \__/ /__/     \__\  |__|     |_______|   //////////////////////////////////////////////////
	
	function parseIntoNumberAndUnit(val) {
		var v = String(val);
		if(v == String(parseFloat(v))) {
			return { value: v, unit: (this.unitsAvailable ? this.unitsAvailable[0] : '') };
		} else {
			vNum = String(parseFloat(v));
			vUnit = getUnitsFromValue(v);
			if(this.unitsAvailable) {
				if(this.unitsAvailable.indexOf(vUnit) < 0) vUnit = this.unitsAvailable[0];
			} else
				vUnit = '';
			return { value: vNum, unit: vUnit };
		}
	}
	function getUnitsFromValue(val) {
		vNum = String(parseFloat(val));
		vUnit = String(val).substr(vNum.length).toLowerCase();
		return vUnit;
	}
	function valueForHTML(val) {
		vNum = String(parseFloat(val));
		vUnit = String(val).substr(vNum.length).toLowerCase();
		return vNum + (vUnit ? (' ' + vUnit) : '');
	}
	function valueFromHTML(val) {
		return val.replace(/\s+/, '');
	}
	
	
	function onElementClick() {
		if (!this.isDragOperation) {
			//$.log('click');
			this.edit();
			//return false;
		}
	}
	
	function getDragHelper() {
		var el = $('<div class="x-editable-number-helper"></div>');
		el.css('width', this.element.css('width'));
		el.css('height', this.element.css('height'));
		el.css('margin', this.element.css('margin'));
		el.css('padding', this.element.css('padding'));
		return el;
	}
	
	function onDragStart(event, ui) {
		this.removePlaceholder();
		this.isDragOperation = true;
		
		var vParsed = this._(parseIntoNumberAndUnit)(this.value);
		this.drag.originalValue = parseFloat(vParsed.value);
		this.drag.unit = vParsed.unit;
		
		if(isNaN(this.drag.originalValue)) this.drag.originalValue = 0;
		this.drag.value = this.drag.originalValue;
		
		this.setFlag('DRAGGING');
		this.element.removeClass('x-editable-on').addClass('x-editing');
	}
	function onDrag(event, ui) {
		//this.drag.x = ui.offset.left;
		//this.drag.y = ui.offset.top;
		//this.drag.delta = Math.sqrt((this.drag.x - this.drag.x0)*(this.drag.x - this.drag.x0)  + (this.drag.y - this.drag.y0)*(this.drag.y - this.drag.y0));
		var dx = ui.position.left - ui.originalPosition.left;
		var dy = ui.position.top - ui.originalPosition.top;
		this.drag.delta = dx; //Math.sqrt(dx*dx + dy*dy);
		
		var prevValue = this.drag.value;
		
		this.drag.value = this.drag.originalValue + Math.round(dx / 2);
		if(this.options.pluginOptions.min !== false && this.drag.value < this.options.pluginOptions.min) this.drag.value = this.options.pluginOptions.min;
		if(this.options.pluginOptions.max !== false && this.drag.value > this.options.pluginOptions.max) this.drag.value = this.options.pluginOptions.max;
		
		this.element.html(this.drag.value + (this.drag.unit ? (' ' + this.drag.unit) : ''));
		if(prevValue != this.drag.originalValue) this.fireChange();
	}
	function onDragStop(event, ui) {
		//$.log('drag stop');
		this.element.addClass('x-editable-on').removeClass('x-editing');
		this.isDragOperation = false;
		this.save();
		this.removeFlag('DRAGGING');
		this.drag = {};	// after value has been set in this.save(), clear the drag object
	}
	
	
	
	
	

	function startEditing() {
		// create replcementElement and add to DOM
		if(!this.replacementElement) {
			this.replacementElement = $('<div>').uniqid().html(valueFromHTML(this.element.html()));
			this.element.removeClass('x-editable x-editable-number x-editable-on');
			this.replacementElement.css(this.element.css());
			this.replacementElement.css({ height: 'auto' });
			this.element.addClass('x-editable x-editable-number x-editable-on');
			this.element.data(Berta.Editor.DATA_REPLACEMENT, this.replacementElement.attr('id'));
		}
		this.element.after(this.replacementElement);
		this.element.detach();
		
		// make replacementElement editable
		this.replacementElement.addClass('x-editing');
		this.replacementElement.attr('contentEditable', true);
		this._(enableEditingOptions)(this.replacementElement);
		this.replacementElement.focus();
		
		// init the mechanism to know when the editing has finished
		// set it after a timeout to avoid catching the initial click event
		setTimeout(this._(addFinishTrigger), 1);

		// select all contents
		if (window.getSelection) {// FIREFOX
			var r = document.createRange();
			r.setStart(this.replacementElement[0], 0);
			r.setEnd(this.replacementElement[0], this.replacementElement[0].childNodes.length);
			window.getSelection().addRange(r);
		} else {// SAFARI
			var sel = window.getSelection();
			var r = document.createRange();
			range.selectNodeContents(this.replacementElement[0]);
			sel.removeAllRanges();
			sel.addRange(r);
		}
	}
	function stopEditing() {
		this._(removeFinishTrigger)();
		this._(disableEditingOptions)(this.replacementElement);
		this.replacementElement.removeClass('x-editing');
		this.replacementElement.attr('contentEditable', false);
	}
	
	
	
	
	
	
	

	function onSaveComplete(isAfterDragging, response) {
		
		if (response.error) {
			if (isAfterDragging) {
				this.cancel(); // no point asking to continue, if it was just a dragging operation
			} else {
				this.askIfCancelOnSaveErorr(response.errorMessage, this._(this.cancel), this._(this.edit));
			}
		} else {
			this.value = response.json.values[this.restVarName];
			this.element.html(valueForHTML(this.value));
			
			Berta.Editor.prototype.hidePanelContext.call(this);
			this.fireSave();
			
			// update original value after calling the callback, so the callback can distinguish between the two
			this.originalValue = this.value;
			
			this.cleanup();
		}
	}
	
	
	
	
	
	function addFinishTrigger() {
		// adds click event to the body that checks if the user has clicked outside the replacementElement
		this._onBodyClick = this._(onBodyClick);
		$('html')[0].addEventListener('click', this._onBodyClick, true); // add to capturing phase
		//$(document.body).bind('click' + this.namespace(), { self: this }, onBodyClick);
	}
	function removeFinishTrigger() {
		// adds click event to the body that checks if the user has clicked outside the replacementElement
		$('html')[0].removeEventListener('click', this._onBodyClick, true);
		this._onBodyClick = null;
		//$(document.body).unbind('click' + this.namespace());
	}
	function onBodyClick(event) {
		// captures the click.Berta.Editor.RTE event and checks if the click has happened
		// outside the replacementElement. if yes, then stopEditing()
		var trg = $(event.target);
		//console.debug(trg, this.options.context);
		
		if(this.options.context != 'panel' && trg.xInPanelOrPopups()) {
			return true;
		} else {
			event.stopPropagation();
			event.preventDefault();
			var editorId = this.replacementElement.attr('id');
			if(trg.attr('id') != editorId && trg.parents('#' + editorId).length == 0) {
				this.save();
			}
			return false;
		}
	}
	
	
	// EFFECTUATING OPTIONS ...
	
	function enableEditingOptions($el) {
		$el.bind('keypress' + this.namespace() + '.options', this._(onKeypress));
		$el.bind('paste' + this.namespace() + '.options', this._(onPasteHTMLCheck));
	};
	function disableEditingOptions($el) {
		// remove all listeners attached by enableEditingOptions
		$el.unbind(this.namespace() + '.options');
		
	};
	
	function onKeypress(event) {
		//$.log(event.which != 13);
		//console.debug(event.which, event.keyCode);
		return (event.which >= 48 && event.which <= 57 || event.which == 46 || event.which == 8) && event.which != 13; // event is cancelled if enter is pressed
	};
	
	var htmlCheckTimeout;
	function onPasteHTMLCheck(event) {
		htmlCheckTimeout = setTimeout(this._(htmlCheck), 1);
	}
	function htmlCheck() {
		var elHtml = this.replacementElement.html();
		elHtml = elHtml.replace(this.nonAllowedCharsRegExp, '');
		this.replacementElement.html(elHtml);
	}
		
	
}()));

